using Pinwheel.LSerialize;

namespace Pinwheel.Kernel
{
    public partial class Client
    {
        private ClientSocket m_ClientSocket;

        public Client()
        {
            m_ClientSocket = new ClientSocket(this);
            m_Rpc = new Rpc();
            m_Connection = null!;
        }

        public void Connect(string ip, int port)
        {
            m_ClientSocket.Connect(ip, port);
        }

        public void Disconnect()
        {
            Assert.Check(m_Connection != null, $"[Client]Disconnect connection is null");
            m_Connection.Close();
        }
        
        public async Task<IMessage> Call(string method, IMessage message, int timeout = 5000)
        {
            return await Call(method, MessageType.Client, message, timeout);
        }

        public async Task<IMessage> Call(string method, MessageType type, IMessage message, int timeout = 5000)
        {
            var msg = new ServiceMessage();
            msg.SetType(type);
            msg.SetMethod(method);
            msg.SetMessage(message);
            
            var tcs = m_Rpc.HandleSend(ref msg);
            Send(ref msg);
            
            var timeoutTask = Task.Delay(timeout);
            var completedTask = await Task.WhenAny(tcs.Task, timeoutTask);
            
            if (completedTask == timeoutTask)
            {
                tcs.TrySetException(new TimeoutException($"Call operation timed out! session: {msg.Session}."));
                throw new TimeoutException($"Call operation timed out! session: {msg.Session}.");
            }

            return await tcs.Task;
        }

        public void Send(string method, IMessage message)
        {
            Send(method, MessageType.Client, message);
        }

        public void Send(string method, MessageType type, IMessage message)
        {
            var msg = new ServiceMessage();
            msg.SetType(type);
            msg.SetMethod(method);
            msg.SetMessage(message);
            Send(ref msg);
        }

        public void Send(ref ServiceMessage message)
        {
            var packet = new NetPacket();
            packet.SetType((byte) message.Type);
            packet.SetSession(message.Session);
            packet.SetData(LSerializer.Serialize(ref message));
            Send(ref packet);
        }

        public void Send(ref NetPacket packet)
        {
            Assert.Check(m_Connection != null, $"[Client]Send connection is null");
            if (null! == m_Connection) return;
            m_Connection.SendAsync(packet);
        }

        private async ValueTask<IMessage> OnReceiveRequest(ServiceMessage message)
        {
            throw new NotImplementedException();
        }
    }
}